#include "vfsmonitor.h"

#include "vfs_monitor.skel.h"

#include <iostream>
#include <iomanip>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include <unistd.h>
#include <signal.h>

//#define NAME_MAX 256
#define MAX_PATH_DEPTH 16
#define MAX_COMM 4096

enum event_type {
    FILE_CREATE,
    FILE_DELETE,
    FILE_RENAME,
    FILE_CONTENT_CHANGED,
    FILE_PERM_CHANGE,
    MOUNT_ADDED,
    MOUNT_REMOVED
};

struct path_info {
    int depth; // Depth of the next path component
    int len; // Length of the path component
    uint64_t dev; // Device ID
    uint64_t ino; // Inode number
    char name[NAME_MAX];
    void *holder; // dentry in kernel, don't use in user space
};

struct event {
    uint32_t pid;
    char comm[MAX_COMM];
    uint32_t type;
    uint64_t dev;
    struct path_info src_path[MAX_PATH_DEPTH];
    uint64_t src_ino;
    struct path_info dst_path[MAX_PATH_DEPTH];
    uint64_t dst_ino;
};

int handle_event(void *ctx, void *data, long unsigned int size) {
    auto obj = (VFSMonitor *)ctx;
    auto evt = static_cast<event*>(data);
    uint32_t pid = evt->pid;
    uint32_t type = evt->type;
    std::string command = evt->comm;
    uint64_t srcDev = 0;
    uint64_t dstDev = 0;
    uint64_t srcIno = 0;
    uint64_t dstIno = 0;
    std::string srcPath;
    std::string dstPath;
    if (type == MOUNT_ADDED || type == MOUNT_REMOVED) {
        std::string devName = evt->src_path[0].name;
        uint64_t devNum = evt->dev;
        std::cout << "mount event" << type << devNum << evt->src_path[0].name << std::endl;
        obj->mountEvent(pid, command, (type == MOUNT_ADDED)?true:false, devName, devNum);
        return 0;
    }
    if (evt->src_path[0].depth > 0 && evt->src_path[0].depth <= MAX_PATH_DEPTH) {
        srcIno = evt->src_ino;
        srcDev = evt->dev;
        for (int i = MAX_PATH_DEPTH - evt->src_path[0].depth; i < MAX_PATH_DEPTH; i++) {
            srcPath += "/";
            srcPath += evt->src_path[i].name;
        }
    }
    if (evt->type == FILE_RENAME && evt->dst_path[0].depth > 0 && evt->dst_path[0].depth <= MAX_PATH_DEPTH) {
        dstIno = evt->dst_ino;
        dstDev = evt->dev;
        for (int i = MAX_PATH_DEPTH - evt->dst_path[0].depth; i < MAX_PATH_DEPTH; i++) {
            dstPath += "/";
            dstPath += evt->dst_path[i].name;
        }
    }

    obj->fileEvent(pid, command, type, srcDev, srcIno, srcPath, dstDev, dstIno, dstPath);

    return 0;
}

VFSMonitor::VFSMonitor(QObject *parent)
    : QObject{parent}
{

}

void VFSMonitor::startMonitor()
{
    struct ring_buffer *rb = nullptr;
    struct vfs_monitor_bpf *obj = vfs_monitor_bpf__open_and_load();
    if (!obj) {
        std::cerr << "Failed to open and load BPF object\n";
        return;
    }

    int ret = vfs_monitor_bpf__attach(obj);
    if (ret) {
        std::cerr << "Failed to attach BPF program\n";
        vfs_monitor_bpf__destroy(obj);
        return;
    }

    rb = ring_buffer__new(bpf_map__fd(obj->maps.events), handle_event, (void *)this, nullptr);
    if (!rb) {
        std::cerr << "Failed to create ring buffer\n";
        vfs_monitor_bpf__detach(obj);
        vfs_monitor_bpf__destroy(obj);
        return;
    }

    std::cout << "Monitoring VFS operations...\n";
    while (true) {
        //std::cout << "Polling...\n";
        ring_buffer__poll(rb, 100);
    }

    ring_buffer__free(rb);
    vfs_monitor_bpf__detach(obj);
    vfs_monitor_bpf__destroy(obj);
}
